// Network stuff for HaxServ
//
// Written by: Test_User <hax@andrewyu.org>
//
// This is free and unencumbered software released into the public
// domain.
//
// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any
// means.
//
// In jurisdictions that recognize copyright laws, the author or authors
// of this software dedicate any and all copyright interest in the
// software to the public domain. We make this dedication for the benefit
// of the public at large and to the detriment of our heirs and
// successors. We intend this dedication to be an overt act of
// relinquishment in perpetuity of all present and future rights to this
// software under copyright law.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

#include <string.h>
#include <limits.h>
#include <stdlib.h>

#include "network.h"
#include "tls.h"
#include "config.h"

char channel_mode_types[UCHAR_MAX+1] = {
	['v'] = MODE_TYPE_USERS,
	['h'] = MODE_TYPE_USERS,
	['o'] = MODE_TYPE_USERS,
	['a'] = MODE_TYPE_USERS,
	['q'] = MODE_TYPE_USERS,
	['b'] = MODE_TYPE_MULTIPLE,
	['e'] = MODE_TYPE_MULTIPLE,
	['I'] = MODE_TYPE_MULTIPLE,
	['c'] = MODE_TYPE_NOARGS,
	['d'] = MODE_TYPE_REPLACE,
	['f'] = MODE_TYPE_REPLACE,
	['g'] = MODE_TYPE_MULTIPLE,
	['i'] = MODE_TYPE_NOARGS,
	['j'] = MODE_TYPE_REPLACE,
	['k'] = MODE_TYPE_REPLACE,
	['l'] = MODE_TYPE_REPLACE,
	['m'] = MODE_TYPE_NOARGS,
	['n'] = MODE_TYPE_NOARGS,
	['p'] = MODE_TYPE_NOARGS,
	['r'] = MODE_TYPE_NOARGS,
	['s'] = MODE_TYPE_NOARGS,
	['t'] = MODE_TYPE_NOARGS,
	['u'] = MODE_TYPE_NOARGS,
	['w'] = MODE_TYPE_MULTIPLE,
	['z'] = MODE_TYPE_NOARGS,
	['A'] = MODE_TYPE_NOARGS,
	['B'] = MODE_TYPE_NOARGS,
	['C'] = MODE_TYPE_NOARGS,
	['D'] = MODE_TYPE_NOARGS,
	['E'] = MODE_TYPE_REPLACE,
	['F'] = MODE_TYPE_REPLACE,
	['G'] = MODE_TYPE_NOARGS,
	['H'] = MODE_TYPE_REPLACE,
	['J'] = MODE_TYPE_REPLACE,
	['K'] = MODE_TYPE_NOARGS,
	['L'] = MODE_TYPE_REPLACE,
	['M'] = MODE_TYPE_NOARGS,
	['N'] = MODE_TYPE_NOARGS,
	['O'] = MODE_TYPE_NOARGS,
	['P'] = MODE_TYPE_NOARGS,
	['Q'] = MODE_TYPE_NOARGS,
	['R'] = MODE_TYPE_NOARGS,
	['S'] = MODE_TYPE_NOARGS,
	['T'] = MODE_TYPE_NOARGS,
	['X'] = MODE_TYPE_MULTIPLE,
};

int privmsg(struct string source, struct string target, size_t num_message_parts, struct string message[num_message_parts]) {
	if (!STRING_EQ(target, STRING("1HC000001"))) { // if not sending to our one local user
		if (source.len != 0) {
			SEND(STRING(":"));
			SEND(source);
			SEND(STRING(" PRIVMSG "));
		} else {
			SEND(STRING("PRIVMSG "));
		}

		SEND(target);
		SEND(STRING(" :"));
		for (size_t i = 0; i < num_message_parts; i++)
			SEND(message[i]);
		SEND(STRING("\n"));
	} else {
		goto privmsg_client;
	}

	if (target.data[0] == '#') {
		struct channel_info *channel = get_table_index(channel_list, target);
		if (channel && has_table_index(channel->user_list, STRING("1HC000001")))
			goto privmsg_client;
	}

	return 0;

	privmsg_client:

	if (source.len != 0) {
		SENDCLIENT(STRING(":"));
		// TODO: Proper lookups of users and such
		if (STRING_EQ(source, STRING("1HC000000"))) {
			SENDCLIENT(nick);
			SENDCLIENT(STRING("!"));
			SENDCLIENT(nick);
			SENDCLIENT(STRING("@"));
			SENDCLIENT(hostmask);
		} else if (STRING_EQ(source, STRING("1HC"))) {
			SENDCLIENT(server_name);
		} else {
			SENDCLIENT(source);
		}
		SENDCLIENT(STRING(" PRIVMSG "));
	} else {
		SENDCLIENT(STRING(":"));
		SENDCLIENT(server_name);
		SENDCLIENT(STRING(" PRIVMSG "));
	}

	if (STRING_EQ(target, STRING("1HC000001")))
		SENDCLIENT(client_nick);
	else
		SENDCLIENT(target);

	SENDCLIENT(STRING(" :"));
	for (size_t i = 0; i < num_message_parts; i++)
		SENDCLIENT(message[i]);
	SENDCLIENT(STRING("\r\n"));

	return 0;
}

int remove_user(struct string uid, struct string reason) { // If disconnecting the local client, set client_connected = 0 *before* calling this
	struct user_info *info = get_table_index(user_list, uid);
	if (!info)
		return 1;

	int send_client = client_connected;
	for (uint64_t i = 0; i < channel_list.len; i++) { // TODO: Use channel list attached to the user (doesn't exist yet)
		struct channel_info *chan_info = channel_list.array[i].ptr;
		if (has_table_index(chan_info->user_list, uid)) {
			if (send_client && has_table_index(chan_info->user_list, STRING("1HC000001"))) {
				SENDCLIENT(STRING(":"));
				SENDCLIENT(info->nick);
				SENDCLIENT(STRING("!"));
				SENDCLIENT(info->ident);
				SENDCLIENT(STRING("@"));
				SENDCLIENT(info->vhost);
				if (reason.len != 0) {
					SENDCLIENT(STRING(" QUIT :"));
					SENDCLIENT(reason);
					SENDCLIENT(STRING("\r\n"));
				} else {
					SENDCLIENT(STRING(" QUIT\r\n"));
				}

				send_client = 0;
			}

			remove_table_index(&(chan_info->user_list), uid);
		}
	}

	remove_table_index(&user_list, uid);

	free(info->server.data);
	free(info->nick.data);
	free(info->opertype.data);
	free(info->metadata.array);
	free(info->realname.data);
	free(info->hostname.data);
	free(info->ip.data);
	free(info->ident.data);
	free(info->vhost.data);
	free(info);

	return 0;
}
